package ejb.searchEngine;


import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.joda.time.DateTime;

import ejb.contentManagement.ExcursionAvailabilityBean;
import ejb.contentManagement.ExcursionBean;
import ejb.contentManagement.FlightBean;
import ejb.contentManagement.HotelAvailabilityBean;
import ejb.contentManagement.HotelBean;
import ejb.contentManagement.LocationMgr;
import ejb.contentManagement.PackageBean;
import ejb.dto.ExcursionAvailabilityDTO;
import ejb.dto.ExcursionDTO;
import ejb.dto.FlightDTO;
import ejb.dto.HotelAvailabilityDTO;
import ejb.dto.HotelDTO;
import ejb.dto.PackageDTO;
import ejb.dto.SearchDTO;
import entities.Excursion;
import entities.ExcursionAvailability;
import entities.Flight;
import entities.Hotel;
import entities.HotelAvailability;
import entities.Location;
import entities.Period;


@Stateless
public class PackageSearchBean implements PackageSearch {
	
	LocationMgr location;
	
	@PersistenceContext
    private EntityManager em;
	
	/**
	 * Return list of PackageDTO after search on different parameters
	 */
	@Override
	public List<PackageDTO> searchByParameters(SearchDTO parameters) {
		List<PackageDTO> results = new ArrayList<PackageDTO>();
		List<entities.Package> list = null;
		
		if(parameters.getFromDate()!=null) {
			List<Period> periods = em.createNamedQuery("Period.findBySingleDate", Period.class).setParameter("d", parameters.getFromDate(), javax.persistence.TemporalType.TIMESTAMP).getResultList();
			if(periods.size() > 0) {	
				if(parameters.getLocationId()>0) {
					Location location = em.find(Location.class, parameters.getLocationId());
					list = em.createNamedQuery("Package.findByPeriodAndLocation", entities.Package.class).setParameter("pe", periods).setParameter("l", location).getResultList();
				}
				else {
					list = em.createNamedQuery("Package.findByPeriod", entities.Package.class).setParameter("pe", periods).getResultList();
				}
			}
			else {
				return results;
			}

		}
		else {
			if(parameters.getLocationId()>0) {
				Location location = em.find(Location.class, parameters.getLocationId());
				list = em.createNamedQuery("Package.findByLocation", entities.Package.class).setParameter("l", location).getResultList();
			}
			else {
				list = em.createNamedQuery("Package.findAll", entities.Package.class).getResultList();
			}
		}
		for(entities.Package p : list) {
			results.add(PackageBean.convertToDTO(p));
		}
		return results;
	}
	
	/**
	 * Return sample list of PackageDTO
	 */
	@Override
	public List<PackageDTO> getSamplePackages() {
		List<entities.Package> list = em.createNamedQuery("Package.getSamples", entities.Package.class).setMaxResults(3).getResultList();
		List<PackageDTO> newList = new ArrayList<PackageDTO>();
		for(entities.Package p : list) {
			newList.add(PackageBean.convertToDTO(p));
		}
		return newList;
	}

	/**
	 * Return available Excursions of one Package, based on Period, Location and Availability of Excursion
	 */
	@Override
	public List<ExcursionDTO> getAvailableExcursions(int packageId, boolean allResults) {
		entities.Package p = null;
		List<ExcursionDTO> newList = new ArrayList<ExcursionDTO>();
		try {
			p = em.find(entities.Package.class, packageId);
		}
		catch (Exception e) {
			return newList;
		}
		//Loose period: +-1 day on default period
		Date start = new DateTime(p.getPeriod().getFromDate()).minusDays(1).toDate();
		Date end = new DateTime(p.getPeriod().getToDate()).plusDays(1).toDate();
		List<Excursion> allExcursions = new ArrayList<Excursion>(p.getExcursions());
		allExcursions.add(p.getExcursion());
		for(Excursion e : allExcursions) {
			List<ExcursionAvailabilityDTO> availability = new ArrayList<ExcursionAvailabilityDTO>();
			for(ExcursionAvailability ea : e.getExcursionAvailabilities()) {
				if(ea.getRemaining()>0 && ea.getFromDate().before(end) && ea.getToDate().after(start)) {
					availability.add(ExcursionAvailabilityBean.convertToDTO(ea));
				}
			}
			if(!availability.isEmpty() || allResults) {
				ExcursionDTO toAdd = ExcursionBean.convertToDTO(e);
				toAdd.setExcursionAvailabilities(availability);
				newList.add(toAdd);
			}
		}
		return newList;
	}
	
	/**
	 * Return available Hotels of one Package, based on Period, Location and Availability of Hotel
	 */
	@Override
	public List<HotelDTO> getAvailableHotels(int packageId, boolean allResults) {
		
		entities.Package p = em.find(entities.Package.class, packageId);
		List<HotelDTO> newList = new ArrayList<HotelDTO>();
		//Loose period: +-1 day on default period
		Date start = new DateTime(p.getPeriod().getFromDate()).minusDays(1).toDate();
		Date end = new DateTime(p.getPeriod().getToDate()).plusDays(1).toDate();
		
		float minPrice = 0;
		List<Hotel> allHotels = new ArrayList<Hotel>(p.getHotels());
		allHotels.add(p.getHotel());
		for(Hotel h : allHotels) {
			List<HotelAvailabilityDTO> availability = new ArrayList<HotelAvailabilityDTO>();
			for(HotelAvailability ha : h.getHotelAvailabilities()) {
				if(ha.getRemaining()>0 && (ha.getFromDate().before(end) && ha.getToDate().after(start))) {
					if(minPrice == 0 || ha.getPrice()<minPrice) {
						minPrice = ha.getPrice();
					}
					availability.add(HotelAvailabilityBean.convertToDTO(ha));
				}
			}
			if(minPrice!=0 || allResults) {
				HotelDTO toAdd = HotelBean.convertToDTO(h);
				toAdd.setHotelAvailabilities(availability);
				toAdd.setMinPrice(minPrice);
				newList.add(toAdd);
			}
		}
		return newList;
	}
	
	/**
	 * Return available depart Flights of one Package, based on Period, Location and Availability of Flight
	 */
	@Override
	public List<FlightDTO> getFlightsTo(int packageId, boolean allResults) {
		entities.Package p = em.find(entities.Package.class, packageId);
		List<FlightDTO> newList = new ArrayList<FlightDTO>();
		//Loose period: +-1 day on default period
		Date start = new DateTime(p.getPeriod().getFromDate()).minusDays(1).toDate();
		Date end = new DateTime(p.getPeriod().getToDate()).plusDays(1).toDate();
		FlightDTO toAdd = null;
		for(Flight f : p.getFlights1()) {
			if(allResults || (f.getRemaining()>0 && f.getDepartureTime().after(start) && f.getDepartureTime().before(end))) {
				toAdd = FlightBean.convertToDTO(f);
				if(!((f.getRemaining()>0 && f.getDepartureTime().after(start) && f.getDepartureTime().before(end)))) {
					toAdd.setExtra("Volo non disponibile");
				}
				else {
					toAdd.setExtra(f.getRemaining() + " posti ancora disponibili");
				}
				newList.add(toAdd);
			}
		}
		return newList;
	}
	
	/**
	 * Return available return Flights of one Package, based on Period, Location and Availability of Flight
	 */
	@Override
	public List<FlightDTO> getFlightsFrom(int packageId, boolean allResults) {
		entities.Package p = em.find(entities.Package.class, packageId);
		List<FlightDTO> newList = new ArrayList<FlightDTO>();
		//Loose period: +-1 day on default period
		Date start = new DateTime(p.getPeriod().getFromDate()).minusDays(1).toDate();
		Date end = new DateTime(p.getPeriod().getToDate()).plusDays(1).toDate();
		FlightDTO toAdd = null;
		for(Flight f : p.getFlights2()) {
			if(allResults || (f.getRemaining()>0 && f.getDepartureTime().after(start) && f.getDepartureTime().before(end))) {
				toAdd = FlightBean.convertToDTO(f);
				if(!((f.getRemaining()>0 && f.getDepartureTime().after(start) && f.getDepartureTime().before(end)))) {
					toAdd.setExtra("Volo non disponibile");
				}
				else {
					toAdd.setExtra(f.getRemaining() + " posti ancora disponibili");
				}
				newList.add(toAdd);
			}
		}
		return newList;
	}
	
	/**
	 * Return all available depart Flights of one Package, based on Period and Location
	 * @param locationId
	 * @return all available depart Flights of one Package
	 */
	public List<FlightDTO> getFlightsTo(int locationId) {
		Location location = em.find(Location.class, locationId);
		List<Flight> list = em.createNamedQuery("Flight.findFlightsTo", Flight.class).setParameter("a", location.getAirports()).getResultList(); 
		List<FlightDTO> newList = new ArrayList<FlightDTO>();
		for(Flight f : list) {
			if(f.getRemaining()>0) {
				newList.add(FlightBean.convertToDTO(f));
			}
		}
		return newList;
	}
	
	/**
	 * Return all available return Flights of one Package, based on Period and Location
	 * @param locationId
	 * @return all available return Flights of one Package
	 */
	public List<FlightDTO> getFlightsFrom(int locationId) {
		Location location = em.find(Location.class, locationId);
		List<Flight> list = em.createNamedQuery("Flight.findFlightsFrom", Flight.class).setParameter("a", location.getAirports()).getResultList(); 
		List<FlightDTO> newList = new ArrayList<FlightDTO>();
		for(Flight f : list) {
			if(f.getRemaining()>0) {
				newList.add(FlightBean.convertToDTO(f));
			}
		}
		return newList;
	}
	
}
